package com.htg.util;


import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

public class UtilTopology {
    private static final double EARTH_RADIUS_KM = 6371.0;
    /**
     * 计算两点距离
     *
     * @param lat1
     * @param lon1
     * @param lat2
     * @param lon2
     * @return 返回单位 km
     */
    public static double haversine(double lat1, double lon1, double lat2, double lon2) {
        // 将经纬度转换为弧度
        double lat1Rad = Math.toRadians(lat1);
        double lon1Rad = Math.toRadians(lon1);
        double lat2Rad = Math.toRadians(lat2);
        double lon2Rad = Math.toRadians(lon2);

        // 计算差值
        double deltaLat = lat2Rad - lat1Rad;
        double deltaLon = lon2Rad - lon1Rad;

        // 应用Haversine公式
        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
                Math.cos(lat1Rad) * Math.cos(lat2Rad) *
                        Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        // 计算距离
        return EARTH_RADIUS_KM * c;
    }



    /**
     * 获取两条线段的交点
     *
     * @param lsegA
     * @param lsegB
     * @return 交点坐标
     */
    public static float[] getCrossPoint(float[] lsegA, float[] lsegB) {
        float x;
        float y;
        float x1 = lsegA[0];
        float y1 = lsegA[1];
        float x2 = lsegA[2];
        float y2 = lsegA[3];
        float x3 = lsegB[0];
        float y3 = lsegB[1];
        float x4 = lsegB[2];
        float y4 = lsegB[3];
        float k1 = Float.MAX_VALUE;
        float k2 = Float.MAX_VALUE;
        boolean flag1 = false;
        boolean flag2 = false;

        if ((x1 - x2) == 0)
            flag1 = true;
        if ((x3 - x4) == 0)
            flag2 = true;

        if (!flag1)
            k1 = (y1 - y2) / (x1 - x2);
        if (!flag2)
            k2 = (y3 - y4) / (x3 - x4);

        if (k1 == k2)
            return null;

        if (flag1) {
            if (flag2)
                return null;
            x = x1;
            if (k2 == 0) {
                y = y3;
            } else {
                y = k2 * (x - x4) + y4;
            }
        } else if (flag2) {
            x = x3;
            if (k1 == 0) {
                y = y1;
            } else {
                y = k1 * (x - x2) + y2;
            }
        } else {
            if (k1 == 0) {
                y = y1;
                x = (y - y4) / k2 + x4;
            } else if (k2 == 0) {
                y = y3;
                x = (y - y2) / k1 + x2;
            } else {
                x = (k1 * x2 - k2 * x4 + y4 - y2) / (k1 - k2);
                y = k1 * (x - x2) + y2;
            }
        }
        if (between(x1, x2, x) && between(y1, y2, y) && between(x3, x4, x) && between(y3, y4, y)) {
            return new float[]{x, y};
        } else {
            return null;
        }
    }

    public static boolean between(float a, float b, float target) {
        if (target >= a - 0.01 && target <= b + 0.01 || target <= a + 0.01 && target >= b - 0.01)
            return true;
        else
            return false;
    }



    //
    public static double distanceTwoPoint(double[] point1, double[] point2) {
        double x1 = point1[0];
        double y1 = point1[1];
        double x2 = point2[0];
        double y2 = point2[1];
        return distanceTwoPoint(x1, y1, x2, y2);
    }

    public static double distanceTwoPoint(double x1, double y1, double x2, double y2) {
        return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    }


    /**
     * 求点到线段的距离
     *
     * @param x1 线段的一个点的x坐标
     * @param y1 线段的一个点的y坐标
     * @param x2 线段的另外一个点的x坐标
     * @param y2 线段的另外一个点的y坐标
     * @param x0 点的x坐标
     * @param y0 点的y坐标
     * @return
     */
    public static double distancePointToLineSegment(float x1, float y1, float x2, float y2, float x0,
                                                    float y0) {

        double space = 0;

        double a, b, c;

        a = distanceTwoPoint(x1, y1, x2, y2);// 线段的长度

        b = distanceTwoPoint(x1, y1, x0, y0);// (x1,y1)到点的距离

        c = distanceTwoPoint(x2, y2, x0, y0);// (x2,y2)到点的距离

        if (c <= 0.000001 || b <= 0.000001) {

            space = 0;

            return space;

        }

        if (a <= 0.000001) {
            space = b;
            return space;
        }

        if (c * c >= a * a + b * b) {

            space = b;

            return space;

        }

        if (b * b >= a * a + c * c) {

            space = c;

            return space;

        }

        double p = (a + b + c) / 2;// 半周长

        double s = Math.sqrt(p * (p - a) * (p - b) * (p - c));// 海伦公式求面积

        space = 2 * s / a;// 返回点到线的距离（利用三角形面积公式求高）

        return space;

    }


    /**
     *
     * @param ALon
     * @param ALat
     * @param ps
     * @return
     */
    public static boolean isGeoPointInPolygon(double ALon, double ALat, List<double[]> ps) {
        int iSum, iCount, iIndex;
        double dLon1 = 0, dLon2 = 0, dLat1 = 0, dLat2 = 0, dLon;
        if (ps.size() < 3) {
            return false;
        }
        iSum = 0;
        iCount = ps.size();
        for (iIndex = 0; iIndex < iCount; iIndex++) {
            if (iIndex == iCount - 1) {
                dLon1 = ps.get(iIndex)[0];
                dLat1 = ps.get(iIndex)[1];
                dLon2 = ps.get(0)[0];
                dLat2 = ps.get(0)[1];
            } else {
                dLon1 = ps.get(iIndex)[0];
                dLat1 = ps.get(iIndex)[1];
                dLon2 = ps.get(iIndex + 1)[0];
                dLat2 = ps.get(iIndex + 1)[1];
            }
            // 以下语句判断A点是否在边的两端点的水平平行线之间，在则可能有交点，开始判断交点是否在左射线上
            if (((ALat >= dLat1) && (ALat < dLat2)) || ((ALat >= dLat2) && (ALat < dLat1))) {
                if (Math.abs(dLat1 - dLat2) > 0) {
                    //得到 A点向左射线与边的交点的x坐标：
                    dLon = dLon1 - ((dLon1 - dLon2) * (dLat1 - ALat)) / (dLat1 - dLat2);
                    // 如果交点在A点左侧（说明是做射线与 边的交点），则射线与边的全部交点数加一：
                    if (dLon < ALon) {
                        iSum++;
                    }
                }
            }
        }
        if ((iSum % 2) != 0) {
            return true;
        }
        return false;
    }

    /**
     * 目前 只考虑 中国 北半球
     * 获取 两个点 构成的  直线的 角度 0°-360°
     *
     * @param start
     * @param end
     */
    public static double getAngle(double[] start, double[] end) {
        double lonStart = start[0];
        double latStart = start[1];
        double lonEnd = end[0];
        double latEnd = end[1];
        double tan = (latEnd - latStart) / (lonEnd - lonStart);
        double angle = Math.atan(tan);
        double degree = Math.toDegrees(angle);
        return degree;
    }

}
